/*=============================================================================
*
*   Copyright (C) 2020 All rights reserved.
*
*   Filename:		vertex.hpp
*
*   Author: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Date:  2020-01-17 16:52
*
*   Last Editors: Wang Zhecheng - wangzhecheng@yeah.net
*
*   Last modified:	2020-08-04 01:24
*
*   Description:
*
=============================================================================*/
#ifndef VERTEX_H
#define VERTEX_H

#include "shape_interface.hpp"

namespace geoxfem {
namespace geometry {

template <typename DimensionType, typename degree>
struct Vertex : ShapeInterface<DimensionType, degree> {
  DEFINE_KEY(Vertex)

  using Shape      = ShapeInterface<DimensionType, degree>;
  using point_type = typename traits::point_type<DimensionType>::type;
  using Gauss      = model::GaussPoint<DimensionType>;
  using DOF        = model::Dof;

  using DofList = std::unordered_map<std::string, DOF>;

  static constexpr size_t dimension = traits::dimension<DimensionType>::value;
  static constexpr size_t point_num = 0;

  /*constructors and destructor{{{*/

  /**
   * @brief constructor
   *
   * @param _listPoint
   */
  Vertex(const std::vector<point_type>& listPoint)
      : Shape{listPoint}, m_gauss{}, m_listDof{} {
    GEOXFEM_ASSERT(this->size() == 1);
    m_gauss.setCoord(this->front().getCoord());
    this->setIndex(this->getHeadPoint().getIndex());
  }
  Vertex()  = default;
  ~Vertex() = default;
  /*}}}*/
  /*member access{{{*/
  VectorXR getCoord() const {
    return this->getHeadPoint().getCoord();
  }
  _real x() const {
    return this->getHeadPoint().x();
  }
  _real y() const {
    return this->getHeadPoint().y();
  }
  _real z() const {
    return this->getHeadPoint().z();
  }
  _real ux() const {
    auto dof = this->getDof("x0");
    return (dof) ? dof->getCurrValue() : 0;
  }
  _real uy() const {
    auto dof = this->getDof("x1");
    return (dof) ? dof->getCurrValue() : 0;
  }
  _real uz() const {
    auto dof = this->getDof("x2");
    return (dof) ? dof->getCurrValue() : 0;
  }
  Gauss& getGauss() {
    return m_gauss;
  }
  const Gauss& getGauss() const {
    return m_gauss;
  }
  /*}}}*/
  /*misc{{{*/

  Vertex<DimensionType, degree>& toVertex() final override {
    return *this;
  }
  const Vertex<DimensionType, degree>& toVertex() const final override {
    return *this;
  }
  /**
   * @brief get Class Name
   *
   * @return
   */
  std::string getClassName() const final override {
    return std::string("Vertex") + std::to_string(dimension) + "D";
  }
  /**
   * @brief to vertex class. avoid to use dynamic_cast
   *
   * @return
   */
  /* Vertex<dim> *toVertex() final override { return this; } */

  size_t getTopologyDimension() const final override {
    return 0;
  }
  /**
   * @brief plot
   *
   * @param plt
   * @param pt_cfg
   * @param std::string
   */
  void plot(io::DataPloter& plt,
            const std::string&,
            const std::string&) const final override {
    const _real x0 = this->getHeadPoint().x();
    const _real y0 = this->getHeadPoint().y();
    const _real z0 = this->getHeadPoint().z();
    if(!this->ifXfem())
      plt.plotPoint(x0, y0, z0, " w p pt 0 lc rgb \"red\"");
    else
      plt.plotPoint(x0, y0, z0, " w p pt 4 lc rgb \"red\"");
    return;
  }
  /*}}}*/
  /*topology connectivity{{{*/

  std::vector<std::unique_ptr<Shape>> createFacet() const final override {
    return std::vector<std::unique_ptr<Shape>>();
  }
  /*}}}*/
  /*iterator access{{{*/

  typename DofList::iterator getDofBegin() {
    return m_listDof.begin();
  }
  typename DofList::iterator getDofEnd() {
    return m_listDof.end();
  }

  typename DofList::const_iterator getDofBegin() const {
    return m_listDof.begin();
  }
  typename DofList::const_iterator getDofEnd() const {
    return m_listDof.end();
  }
  /*}}}*/
  /*DOF related{{{*/

  void append(const std::string& key, const DOF& dof) {
    m_listDof.emplace(key, dof);
  }
  const DOF* getDof(const std::string& key) const {
    if(m_listDof.find(key) == m_listDof.end())
      return nullptr;
    return &m_listDof.at(key);
  }
  DOF* getDof(const std::string& key) {
    if(m_listDof.find(key) == m_listDof.end())
      return nullptr;
    return &m_listDof.at(key);
  }
  /*}}}*/
  /*xfem related{{{*/

  void setXfem(const bool& value) final override {
    Shape::setXfem(value);
    if(value)
      for(size_t i = 0; i != dimension; ++i)
        append(std::string("Mx") + std::to_string(i),
               model::Dof{this->getIndex()});
  }

  /*}}}*/
private:
  template <size_t dim>
  void add_xfem_dofs() {
    throw "not implented yet";
  }
  template <>
  void add_xfem_dofs<2>() {
    append("Mx", DOF{this->getIndex()});
    append("My", DOF{this->getIndex()});
  }
  template <>
  void add_xfem_dofs<3>() {
    append("Mx", DOF{this->getIndex()});
    append("My", DOF{this->getIndex()});
    append("Mz", DOF{this->getIndex()});
  }
  Gauss   m_gauss;
  DofList m_listDof;
};

}  // namespace geometry

namespace io {

struct vtu_vertex_coord { /*{{{*/
  template <typename DimensionType, typename degree>
  std::string
  operator()(const geometry::ShapeInterface<DimensionType, degree>& shp) const {
    auto vertex = shp.toVertex();
    /* Create an output string stream */
    std::ostringstream streamObj;
    /* Add double to stream */
    streamObj << vertex.x() << " " << vertex.y() << " " << vertex.z() << "\n";
    /* Get string from output string stream */
    return streamObj.str();
  }
};                         /*}}}*/
struct vtu_vertex_deform { /*{{{*/
  template <typename DimensionType, typename degree>
  std::string
  operator()(const geometry::ShapeInterface<DimensionType, degree>& shp) const {
    auto vertex = shp.toVertex();
    /* Create an output string stream */
    std::ostringstream streamObj;
    /* Add double to stream */
    streamObj << vertex.ux() << " " << vertex.uy() << " " << vertex.uz()
              << "\n";
    /* Get string from output string stream */
    return streamObj.str();
  }
};                       /*}}}*/
struct vtu_vertex_pore { /*{{{*/
  template <typename DimensionType, typename degree>
  std::string
  operator()(const geometry::ShapeInterface<DimensionType, degree>& shp) const {
    auto  dof   = shp.toVertex().getDof("pore");
    _real value = (dof) ? dof->getCurrValue() : 0;
    return std::to_string(value) + "\n";
  }
}; /*}}}*/

}  // namespace io

}  // namespace geoxfem

#endif /* VERTEX_H */
